/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/
#include "fh_mshc_lite_dll.h"
/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/
#define EMMC_PHY_VERSION_ID                 0x00
#define EMMC_PHY_INT_EN                     0x04
#define EMMC_PHY_INT_SRC                    0x08
#define EMMC_PHY_CTRL                       0x0C
#define EMMC_PHY_TEST_O                     0x10
#define EMMC_PHY_TEST_I                     0x14
#define EMMC_PHY_IE_CTRL                    0x18
#define EMMC_PHY_DL_CTRL                    0x1C
#define EMMC_PHY_CTRL2                      0x24


#define TUING_TIMEOUT	1000
#define SDHCI_CMD_MAX_TIMEOUT			3200
#define SDHCI_CMD_DEFAULT_TIMEOUT		100
#define SDHCI_READ_STATUS_TIMEOUT		1000
#define PART_ACCESS_MASK	(0x7)

#define USER_AREA_ACCESS		0
#define BOOT_1_ACCESS			1
#define BOOT_2_ACCESS			2


#ifndef CONFIG_AUTO_TUNING_EMMC_AREA
#define CONFIG_AUTO_TUNING_EMMC_AREA BOOT_2_ACCESS
#endif
#define CONFIG_AUTO_TUNING_EMMC_ADDR 0x0


//below cpy from uboot_2016\include\mmc.h
#define SD_VERSION_SD	(1U << 31)
#define MMC_VERSION_MMC	(1U << 30)

#define MAKE_SDMMC_VERSION(a, b, c)	\
	((((u32)(a)) << 16) | ((u32)(b) << 8) | (u32)(c))
#define MAKE_SD_VERSION(a, b, c)	\
	(SD_VERSION_SD | MAKE_SDMMC_VERSION(a, b, c))
#define MAKE_MMC_VERSION(a, b, c)	\
	(MMC_VERSION_MMC | MAKE_SDMMC_VERSION(a, b, c))

#define EXTRACT_SDMMC_MAJOR_VERSION(x)	\
	(((u32)(x) >> 16) & 0xff)
#define EXTRACT_SDMMC_MINOR_VERSION(x)	\
	(((u32)(x) >> 8) & 0xff)
#define EXTRACT_SDMMC_CHANGE_VERSION(x)	\
	((u32)(x) & 0xff)

#define SD_VERSION_3		MAKE_SD_VERSION(3, 0, 0)
#define SD_VERSION_2		MAKE_SD_VERSION(2, 0, 0)
#define SD_VERSION_1_0		MAKE_SD_VERSION(1, 0, 0)
#define SD_VERSION_1_10		MAKE_SD_VERSION(1, 10, 0)

#define MMC_VERSION_UNKNOWN	MAKE_MMC_VERSION(0, 0, 0)
#define MMC_VERSION_1_2		MAKE_MMC_VERSION(1, 2, 0)
#define MMC_VERSION_1_4		MAKE_MMC_VERSION(1, 4, 0)
#define MMC_VERSION_2_2		MAKE_MMC_VERSION(2, 2, 0)
#define MMC_VERSION_3		MAKE_MMC_VERSION(3, 0, 0)
#define MMC_VERSION_4		MAKE_MMC_VERSION(4, 0, 0)
#define MMC_VERSION_4_1		MAKE_MMC_VERSION(4, 1, 0)
#define MMC_VERSION_4_2		MAKE_MMC_VERSION(4, 2, 0)
#define MMC_VERSION_4_3		MAKE_MMC_VERSION(4, 3, 0)
#define MMC_VERSION_4_41	MAKE_MMC_VERSION(4, 4, 1)
#define MMC_VERSION_4_5		MAKE_MMC_VERSION(4, 5, 0)
#define MMC_VERSION_5_0		MAKE_MMC_VERSION(5, 0, 0)
#define MMC_VERSION_5_1		MAKE_MMC_VERSION(5, 1, 0)

#define MMC_MODE_HS		(1 << 0)
#define MMC_MODE_HS_52MHz	(1 << 1)
#define MMC_MODE_4BIT		(1 << 2)
#define MMC_MODE_8BIT		(1 << 3)
#define MMC_MODE_SPI		(1 << 4)
#define MMC_MODE_DDR_52MHz	(1 << 5)
#define MMC_MODE_HS200_1_8V	(1 << 6)
#define MMC_MODE_HS200_1_2V	(1 << 7)
#define MMC_MODE_HS200	(MMC_MODE_HS200_1_8V | MMC_MODE_HS200_1_2V)
#define MMC_MODE_HS400_1_8V	(1 << 8)
#define MMC_MODE_HS400_1_2V	(1 << 9)
#define MMC_MODE_HS400	(MMC_MODE_HS400_1_8V | MMC_MODE_HS400_1_2V)
#define MMC_MODE_HS400ES	(1 << 10)



#define SD_DATA_4BIT	0x00040000

#define IS_SD(x)	((x)->version & SD_VERSION_SD)
#define IS_MMC(x)	((x)->version & MMC_VERSION_MMC)


#define MSHC_MMC_DATA_READ		1
#define MSHC_MMC_DATA_WRITE		2

#define MMC_CMD_GO_IDLE_STATE		0
#define MMC_CMD_SEND_OP_COND		1
#define MMC_CMD_ALL_SEND_CID		2
#define MMC_CMD_SET_RELATIVE_ADDR	3
#define MMC_CMD_SET_DSR			4
#define MMC_CMD_SWITCH			6
#define MMC_CMD_SELECT_CARD		7
#define MMC_CMD_SEND_EXT_CSD		8
#define MMC_CMD_SEND_CSD		9
#define MMC_CMD_SEND_CID		10
#define MMC_CMD_STOP_TRANSMISSION	12
#define MMC_CMD_SEND_STATUS		13
#define MMC_CMD_SET_BLOCKLEN		16
#define MMC_CMD_READ_SINGLE_BLOCK	17
#define MMC_CMD_READ_MULTIPLE_BLOCK	18
#define MMC_CMD_SET_BLOCK_COUNT         23
#define MMC_CMD_WRITE_SINGLE_BLOCK	24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK	25
#define MMC_CMD_ERASE_GROUP_START	35
#define MMC_CMD_ERASE_GROUP_END		36
#define MMC_CMD_ERASE			38
#define MMC_CMD_APP_CMD			55
#define MMC_CMD_SPI_READ_OCR		58
#define MMC_CMD_SPI_CRC_ON_OFF		59
#define MMC_CMD_RES_MAN			62

#define MMC_CMD_SEND_TUNING_BLOCK             19
#define MMC_CMD_SEND_TUNING_BLOCK_HS200       21


#define MMC_CMD62_ARG1			0xefac62ec
#define MMC_CMD62_ARG2			0xcbaea7

#define EXT_CSD_PART_CONF		179	/* R/W */

/* SCR definitions in different words */
#define SD_HIGHSPEED_BUSY	0x00020000
#define SD_HIGHSPEED_SUPPORTED	0x00020000

#define OCR_BUSY		0x80000000
#define OCR_HCS			0x40000000
#define OCR_VOLTAGE_MASK	0x007FFF80
#define OCR_ACCESS_MODE		0x60000000

#define MMC_ERASE_ARG		0x00000000
#define MMC_SECURE_ERASE_ARG	0x80000000
#define MMC_TRIM_ARG		0x00000001
#define MMC_DISCARD_ARG		0x00000003
#define MMC_SECURE_TRIM1_ARG	0x80000001
#define MMC_SECURE_TRIM2_ARG	0x80008000

#define MMC_STATUS_MASK		(~0x0206BF7F)
#define MMC_STATUS_SWITCH_ERROR	(1 << 7)
#define MMC_STATUS_RDY_FOR_DATA (1 << 8)
#define MMC_STATUS_CURR_STATE	(0xf << 9)
#define MMC_STATUS_ERROR	(1 << 19)

#define MMC_STATE_PRG		(7 << 9)



#define MMC_SWITCH_MODE_CMD_SET		0x00 /* Change the command set */
#define MMC_SWITCH_MODE_SET_BITS	0x01 /* Set bits in EXT_CSD byte
						addressed by index which are
						1 in value field */
#define MMC_SWITCH_MODE_CLEAR_BITS	0x02 /* Clear bits in EXT_CSD byte
						addressed by index, which are
						1 in value field */
#define MMC_SWITCH_MODE_WRITE_BYTE	0x03 /* Set target byte to value */

#define MMC_RSP_PRESENT (1 << 0)
#define MMC_RSP_136	(1 << 1)		/* 136 bit response */
#define MMC_RSP_CRC	(1 << 2)		/* expect valid crc */
#define MMC_RSP_BUSY	(1 << 3)		/* card may send busy */
#define MMC_RSP_OPCODE	(1 << 4)		/* response contains opcode */

#define MMC_RSP_NONE	(0)
#define MMC_RSP_R1	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R1b	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \
			MMC_RSP_BUSY)
#define MMC_RSP_R2	(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
#define MMC_RSP_R3	(MMC_RSP_PRESENT)
#define MMC_RSP_R4	(MMC_RSP_PRESENT)
#define MMC_RSP_R5	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)

/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type that only be used in this file  here
 ***************************************************************************/
struct mshc_mmc_cmd {
	ushort cmdidx;
	uint resp_type;
	uint cmdarg;
	uint response[4];
};

struct mshc_mmc_data {
	union {
		char *dest;
		const char *src; /* src buffers don't get written to */
	};
	uint flags;
	uint blocks;
	uint blocksize;
};

/*****************************************************************************
 *  static var & fun;
 *****************************************************************************/
static u32 g_mshc_buf[512/4] = {0};

/*****************************************************************************
 * func below
 *****************************************************************************/
static inline void mshc_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
	writel(val, host->ioaddr + reg);
}

static inline void mshc_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
	writew(val, host->ioaddr + reg);
}

static inline void mshc_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
	writeb(val, host->ioaddr + reg);
}
static inline u32 mshc_sdhci_readl(struct sdhci_host *host, int reg)
{
	return readl(host->ioaddr + reg);
}

static inline u16 mshc_sdhci_readw(struct sdhci_host *host, int reg)
{
	return readw(host->ioaddr + reg);
}

static inline u8 mshc_sdhci_readb(struct sdhci_host *host, int reg)
{
	return readb(host->ioaddr + reg);
}


void phy_tx_dri_clk_sel(struct fh_mshc_lite *p_mshc, u32 sel){

	if(sel == PHY_TX_DRI_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 1, 3 << 1);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 2, 3 << 1);
	}
}


void phy_tx_dri_delay(struct fh_mshc_lite *p_mshc, u32 delay){
	if(delay == PHY_BY_PASS_DELAY_FUNC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 15, 1 << 15);
	}else{
		
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_DL_CTRL, delay, 0xff);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 15, 1 << 15);
	}
}


void phy_tx_dri_inv_set(struct fh_mshc_lite *p_mshc, u32 enable){

	if(enable == PHY_TX_DRI_INV_ENABLE){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 9, 1 << 9);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 9);
	}

}


void phy_tx_for_card_clk_settting(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1)
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 10, 1 << 10);
	else
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 10);
}

void phy_tx_for_drive_clk_gate(struct fh_mshc_lite *p_mshc){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 3 << 1);
}


void phy_rx_data_sample_clk_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	if(sel == PHY_RX_CMD_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 11);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 11, 1 << 11);
	}

}

void phy_rx_data_sample_delay(struct fh_mshc_lite *p_mshc, u32 delay){


	if(delay == PHY_BY_PASS_DELAY_FUNC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 16, 1 << 16);
	}else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_DL_CTRL, delay << 8, 0xff << 8);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 16, 1 << 16);
	}

}


void phy_rx_data_sample_inv_set(struct fh_mshc_lite *p_mshc, u32 enable){

	if(enable == PHY_RX_DATA_SAMPLE_INV_ENABLE){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 13, 1 << 13);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 13);
	}

}


void phy_rx_cmd_sample_clk_sel(struct fh_mshc_lite *p_mshc, u32 sel){

	if(sel == PHY_RX_CMD_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 12);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 12, 1 << 12);
	}
}


void phy_rx_cmd_sample_delay(struct fh_mshc_lite *p_mshc, u32 delay){
	if(delay == PHY_BY_PASS_DELAY_FUNC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 17, 1 << 17);
	}else{
		
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_DL_CTRL, delay << 16, 0xff << 16);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 17, 1 << 17);
	}
}

void phy_rx_cmd_sample_inv_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == PHY_RX_CMD_SAMPLE_INV_ENABLE){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 14, 1 << 14);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 14);
	}

}


void phy_bit_mode_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, sel << 29, 3 << 29);
}


void phy_intr_en_set(struct fh_mshc_lite *p_mshc, u32 val){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_INT_EN, val, 0x0f);
}

void phy_intr_src_set(struct fh_mshc_lite *p_mshc, u32 val){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_INT_SRC, val, 0x0f);
}

void phy_clk_stop_en_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 3, 1 << 3);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1 << 3);
	}
}

void phy_tx_clk_src_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	if(sel == PHY_TX_DRI_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 1, 3 << 1);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 2, 3 << 1);
	}
}


void phy_rx_sample_enable_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 31, 1 << 31);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 28, 1 << 28);
	}

	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 28);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 31);
	}
}

void phy_clear_rxfifo(struct fh_mshc_lite *p_mshc){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 27, 1 << 27);
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 27, 1 << 27);
}

void phy_clkstop_func_enable(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1)
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 3, 1 << 3);
	else
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1 << 3);
}


void phy_func_mode_set(struct fh_mshc_lite *p_mshc){

	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1);
}


void reset_card_setting(struct fh_mshc_lite *p_mshc, u32 enable){

	if(enable  == 1)
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_IE_CTRL, 0, 1 << 9);
	else
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_IE_CTRL, 1 << 9, 1 << 9);
}



void phy_dly_setting(struct fh_mshc_lite *p_mshc, u32 tx_dly, u32 rxdata_dly, u32 rxcmd_dly){

	phy_tx_dri_delay(p_mshc, tx_dly);
	phy_rx_data_sample_delay(p_mshc, rxdata_dly);
	phy_rx_cmd_sample_delay(p_mshc, rxcmd_dly);
	phy_rx_sample_enable_set(p_mshc, 1);
	phy_tx_for_card_clk_settting(p_mshc, 1);
}


void fh_mshc_lite_hw_reset(struct fh_mshc_lite *fh_mshc_obj){
	//ref 6.15.10 H/W Reset Operation
	//__1us__::--200us--::emmc card ok
	//here keep rst_n 10us lowlev, then wait emmc for 500us
	reset_card_setting(fh_mshc_obj , 1);
	mdelay(10);
	reset_card_setting(fh_mshc_obj , 0);
	mdelay(500);
}



void mshc_phy_init(struct fh_mshc_lite *p_mshc, int inv_en){
	phy_rx_sample_enable_set(p_mshc, 0);
	phy_intr_src_set(p_mshc, 0);
	phy_tx_for_drive_clk_gate(p_mshc);
	phy_func_mode_set(p_mshc);

	if(inv_en == INIT_INV_ENABLE){
		phy_tx_dri_inv_set(p_mshc, PHY_TX_DRI_INV_ENABLE);
		phy_rx_data_sample_inv_set(p_mshc, PHY_RX_DATA_SAMPLE_INV_ENABLE);
		phy_rx_cmd_sample_inv_set(p_mshc, PHY_RX_CMD_SAMPLE_INV_ENABLE);
	}
	else{
		phy_tx_dri_inv_set(p_mshc, PHY_TX_DRI_INV_DISABLE);
		phy_rx_data_sample_inv_set(p_mshc, PHY_RX_DATA_SAMPLE_INV_DISABLE);
		phy_rx_cmd_sample_inv_set(p_mshc, PHY_RX_CMD_SAMPLE_INV_DISABLE);
	}

	phy_tx_dri_clk_sel(p_mshc, PHY_TX_DRI_SEL_CLK_EMMC);
	phy_rx_data_sample_clk_sel(p_mshc, PHY_RX_DATA_SEL_CLK_EMMC);
	phy_rx_cmd_sample_clk_sel(p_mshc, PHY_RX_CMD_SEL_CLK_EMMC);
	phy_clk_stop_en_set(p_mshc, 1);
	phy_bit_mode_sel(p_mshc, PHY_MAX_BIT_WIDTH_8);
	phy_dly_setting(p_mshc, PHY_BY_PASS_DELAY_FUNC, PHY_BY_PASS_DELAY_FUNC, PHY_BY_PASS_DELAY_FUNC);
}


static void sdhci_tuning_cmd_done(struct sdhci_host *host, struct mshc_mmc_cmd *cmd)
{
	int i;
	if (cmd->resp_type & MMC_RSP_136) {
		/* CRC is stripped so we need to do some shifting. */
		for (i = 0; i < 4; i++) {
			cmd->response[i] = mshc_sdhci_readl(host,
					SDHCI_RESPONSE + (3-i)*4) << 8;
			if (i != 3)
				cmd->response[i] |= mshc_sdhci_readb(host,
						SDHCI_RESPONSE + (3-i)*4-1);
		}
	} else {
		cmd->response[0] = mshc_sdhci_readl(host, SDHCI_RESPONSE);
	}
}


static void tuning_sdhci_transfer_pio(struct sdhci_host *host, struct mshc_mmc_data *data)
{
	int i;
	char *offs;
	for (i = 0; i < data->blocksize; i += 4) {
		offs = data->dest + i;
		if (data->flags == MSHC_MMC_DATA_READ)
			*(u32 *)offs = mshc_sdhci_readl(host, SDHCI_BUFFER);
		else
			mshc_sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER);
	}
}


static int tuning_sdhci_transfer_data(struct sdhci_host *host, struct mshc_mmc_data *data,
				unsigned int start_addr)
{
	unsigned int stat, rdy, mask, timeout;

	timeout = 1000000;
	rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL;
	mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE;
	do {
		stat = mshc_sdhci_readl(host, SDHCI_INT_STATUS);
		if (stat & SDHCI_INT_ERROR) {
			pr_err("%s: Error detected in status(0x%x)!\n",
			       __func__, stat);
			return -EIO;
		}
		if (stat & rdy) {
			if (!(mshc_sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
				continue;
			mshc_sdhci_writel(host, rdy, SDHCI_INT_STATUS);
			tuning_sdhci_transfer_pio(host, data);
			data->dest += data->blocksize;
		}

		if (timeout-- > 0)
			udelay(10);
		else {
			pr_err("%s: Transfer data timeout\n", __func__);
			//sdhci_dumpregs(host);
			return -ETIMEDOUT;
		}
	} while (!(stat & SDHCI_INT_DATA_END));
	return 0;
}



static int sdhci_tuning_send_command(struct sdhci_host *host, struct mshc_mmc_cmd *cmd,
			      struct mshc_mmc_data *data)
{
	//struct sdhci_host *host = mmc->priv;
	u16 ctrl2;
	u32 mask, flags, mode;
	unsigned int stat = 0;
	int ret = 0;
	int v4_sup = 0;
	/* Timeout unit - ms */
	unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT;
	unsigned int time = 0, start_addr = 0;

	ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
	if (ctrl2 & 0x1000)
		v4_sup = 1;


	mshc_sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
	mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;

	/* We shouldn't wait for data inihibit for stop commands, even
	   though they might use busy signaling */
	//pr_err("%s : cmdindex = %x\n",__func__,cmd->cmdidx);
	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
		mask &= ~SDHCI_DATA_INHIBIT;

	while (mshc_sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
		if (time >= cmd_timeout) {
			if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) {
				cmd_timeout += cmd_timeout;
			} else {
				pr_err("timeout.\n");
				return -ECOMM;
			}
		}
		time++;
		udelay(1000);
	}

	mask = SDHCI_INT_RESPONSE;
	if (!(cmd->resp_type & MMC_RSP_PRESENT))
		flags = SDHCI_CMD_RESP_NONE;
	else if (cmd->resp_type & MMC_RSP_136)
		flags = SDHCI_CMD_RESP_LONG;
	else if (cmd->resp_type & MMC_RSP_BUSY) {
		flags = SDHCI_CMD_RESP_SHORT_BUSY;
		if (data)
			mask |= SDHCI_INT_DATA_END;
	} else
		flags = SDHCI_CMD_RESP_SHORT;

	if (cmd->resp_type & MMC_RSP_CRC)
		flags |= SDHCI_CMD_CRC;
	if (cmd->resp_type & MMC_RSP_OPCODE)
		flags |= SDHCI_CMD_INDEX;
	if (data)
		flags |= SDHCI_CMD_DATA;

	/* Set Transfer mode regarding to data flag */
	if (data != 0) {
		mshc_sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
		mode = SDHCI_TRNS_BLK_CNT_EN;

		if (data->blocks > 1)
			mode |= SDHCI_TRNS_MULTI;

		if (data->flags == MSHC_MMC_DATA_READ)
			mode |= SDHCI_TRNS_READ;


		mshc_sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
				data->blocksize),
				SDHCI_BLOCK_SIZE);
		if(v4_sup)
			sdhci_writel(host, data->blocks, 0);
		else
			sdhci_writew(host, data->blocks, 0x06);
		mshc_sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
	} else if (cmd->resp_type & MMC_RSP_BUSY) {
		mshc_sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
	}
	mshc_sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT);

	mshc_sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND);
	time = 0;
	do {
		stat = mshc_sdhci_readl(host, SDHCI_INT_STATUS);
		if (stat & SDHCI_INT_ERROR)
			break;

		if (time >= SDHCI_READ_STATUS_TIMEOUT) {
			pr_err("%s: Timeout for status update!\n",
					__func__);
			return -ETIMEDOUT;
		}
		time++;
		udelay(10);
	} while ((stat & mask) != mask);

	if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
		sdhci_tuning_cmd_done(host, cmd);
		mshc_sdhci_writel(host, mask, SDHCI_INT_STATUS);
	} else
		ret = -1;

	if (!ret && data)
		ret = tuning_sdhci_transfer_data(host, data, start_addr);

	stat = mshc_sdhci_readl(host, SDHCI_INT_STATUS);
	mshc_sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
	if (!ret) {
		return 0;
	}

	sdhci_reset(host, SDHCI_RESET_CMD);
	sdhci_reset(host, SDHCI_RESET_DATA);
	if (stat & SDHCI_INT_TIMEOUT)
		return -ETIMEDOUT;
	else
		return -ECOMM;

}


int mmc_tuning_send_status(struct sdhci_host *host, int timeout, int mmc_rca)
{
	struct mshc_mmc_cmd cmd;
	int err, retries = 5;

	cmd.cmdidx = MMC_CMD_SEND_STATUS;
	cmd.resp_type = MMC_RSP_R1;
	cmd.cmdarg = mmc_rca << 16;

	while (1) {
		err = sdhci_tuning_send_command(host, &cmd, NULL);
		if (!err) {

			if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR){
					pr_err("switch err..\n");
					return -EBADMSG;
			}
			if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
			    (cmd.response[0] & MMC_STATUS_CURR_STATE) !=
			     MMC_STATE_PRG)
				break;
			else if (cmd.response[0] & MMC_STATUS_MASK) {
				pr_err("Status Error: 0x%08X\n",
					cmd.response[0]);
				return -ECOMM;
			}
		} else if (--retries < 0)
			return err;

		if (timeout-- <= 0)
			break;

		udelay(1000);
	}
	if (timeout <= 0) {
		pr_err("Timeout waiting card ready\n");
		return -ETIMEDOUT;
	}
	return 0;
}

static int sdhci_card_busy(struct sdhci_host *host)
{
    u32 present_state;

	/* Check whether DAT[0] is 0 */
	present_state = mshc_sdhci_readl(host, SDHCI_PRESENT_STATE);

	return !(present_state & SDHCI_DATA_0_LVL_MASK);
}

int _mmc_tuning_switch(struct sdhci_host *host, u8 set,
		u8 index, u8 value, bool send_status)
{

	struct mshc_mmc_cmd cmd;
	int timeout = 1000;
	int ret;

	cmd.cmdidx = MMC_CMD_SWITCH;
	cmd.resp_type = MMC_RSP_R1b;
	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
				 (index << 16) |
				 (value << 8) |
				 set;
	ret = sdhci_tuning_send_command(host, &cmd, NULL);

	do {
		if (!sdhci_card_busy(host))
			break;
		udelay(1000);
	} while (--timeout);
	/* Waiting for the ready status */
	timeout = 1000;
	if (!ret && send_status)
		ret = mmc_tuning_send_status(host, timeout, 1);

	return ret;

}


int mmc_tuning_switch(struct sdhci_host *host, u8 set, u8 index, u8 value)
{
	return _mmc_tuning_switch(host, set, index, value, true);
}


int mmc_tuning_set_blocklen(struct sdhci_host *host, int len)
{
	struct mshc_mmc_cmd cmd;

	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
	cmd.resp_type = MMC_RSP_R1;
	cmd.cmdarg = len;

	return sdhci_tuning_send_command(host, &cmd, NULL);
}



static int mmc_auto_tuning_common_init(struct fh_mshc_lite *p_mshc, u8 *p_u8_extdata){

	struct mshc_lite_plat_info *plat_info;
	struct sdhci_host *host;
	struct mshc_mmc_cmd cmd;
	u32 timing;
	u32 clk;
	u32 timeout = TUING_TIMEOUT;
	struct mshc_mmc_data data = {0};

	int err;
	plat_info = p_mshc->mshc_plat_info;
	host = p_mshc->bind_host;
	if(!host){
		pr_err("none host handle....\n");
		return -1;
	}

	if(!host || !p_mshc || !plat_info){
		pr_err("err got null info...\n");
		pr_err("host : p_mshc : plat_info = %x : %x : %x\n",
		(int)host, (int)p_mshc, (int)plat_info);
		return -2;
	}

	if(plat_info->soc_ctrl_reset)
		plat_info->soc_ctrl_reset(host);
	mshc_phy_init(p_mshc, INIT_INV_ENABLE);
	fh_mshc_tuning_ctrl_init(host);

	clk = 400000;
	timing = MMC_TIMING_MMC_HS;
	fh_mshc_tuning_set_clock(host, clk, timing, MMC_BUS_WIDTH_1, EMMC_ES_MODE_DISABLE);
	cmd.cmdidx =  0;
	cmd.resp_type = MMC_RSP_NONE;
	cmd.cmdarg = 0;
	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}

	do{
		cmd.cmdidx = MMC_CMD_SEND_OP_COND;
		cmd.resp_type = MMC_RSP_R3;
		cmd.cmdarg = 0x40000080;
		sdhci_tuning_send_command(host, &cmd, NULL);
		timeout--;
		if(!timeout)
			break;
		udelay(100);
	}while(!(cmd.response[0] & OCR_BUSY));

	if(!timeout){
		pr_err("try cmd1 timeout\n");
		return -3;
	}

	cmd.cmdidx =  2;
	cmd.resp_type = MMC_RSP_R2;
	cmd.cmdarg = 0;
	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}

	//set rel id
	cmd.cmdidx = 3;
	cmd.cmdarg = 1 << 16;
	cmd.resp_type = MMC_RSP_R6;
	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}
	/* Get the Card-Specific Data */
	cmd.cmdidx = 9;
	cmd.resp_type = MMC_RSP_R2;
	cmd.cmdarg = 1 << 16;

	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}

	//goto sel hs200 mode...
	//sel device..
	cmd.cmdidx = 7;
	cmd.resp_type = MMC_RSP_R1;
	cmd.cmdarg = 1 << 16;
	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}
	//dev is lock? cmd42 to unlock..

	//cmd8 to check if sup hs200..
	cmd.cmdidx = 8;
	cmd.resp_type = MMC_RSP_R1;
	cmd.cmdarg = 0;
	data.dest = (char *)p_u8_extdata;
	data.blocks = 1;
	data.blocksize = 512;
	data.flags = MSHC_MMC_DATA_READ;

	err = sdhci_tuning_send_command(host, &cmd, &data);
	if (err){
		pr_err("!!!!!!raw cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}
	//swtich area....
	p_u8_extdata[EXT_CSD_PART_CONF] &= ~(PART_ACCESS_MASK);
	p_u8_extdata[EXT_CSD_PART_CONF] |= (CONFIG_AUTO_TUNING_EMMC_AREA & PART_ACCESS_MASK);
	err = mmc_tuning_switch(host, EXT_CSD_CMD_SET_NORMAL,
		EXT_CSD_PART_CONF, p_u8_extdata[EXT_CSD_PART_CONF]);
	if (err) {
		pr_err("switch width err, err:%d\n", err);
		return err;
	}

	//reread extdata..
	cmd.cmdidx = 8;
	cmd.resp_type = MMC_RSP_R1;
	cmd.cmdarg = 0;
	data.dest = (char *)p_u8_extdata;
	data.blocks = 1;
	data.blocksize = 512;
	data.flags = MSHC_MMC_DATA_READ;

	err = sdhci_tuning_send_command(host, &cmd, &data);
	if (err){
		pr_err("!!!!!! fix cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
		return err;
	}

	return 0;
}
void translate_mmcid_to_string(char *name, u32 *mmc_id){
	sprintf(name, "%c%c%c%c%c%c", mmc_id[0] & 0xff,
	(mmc_id[1] >> 24), (mmc_id[1] >> 16) & 0xff,
	(mmc_id[1] >> 8) & 0xff, mmc_id[1] & 0xff,
	(mmc_id[2] >> 24) & 0xff);
}

int mshc_auto_get_mmc_cid(struct fh_mshc_lite *p_mshc, u32 *ret_cid, u8 *emmc_name){
	int i;
	int rec_index;
	struct mshc_lite_plat_info *plat_info;
	struct mshc_mmc_cmd cmd;
	int err;
	u32 timing;
	u32 clk;
	u32 rx;
	u32 try_times = 0;
	u32 pass_times = 0;
	u32 time_out = TUING_TIMEOUT;
	struct sdhci_host *host;
	plat_info = p_mshc->mshc_plat_info;
	host = p_mshc->bind_host;

	if (plat_info->soc_ctrl_reset)
		plat_info->soc_ctrl_reset(host);
	mshc_phy_init(p_mshc, INIT_INV_ENABLE);
	fh_mshc_tuning_ctrl_init(host);

	clk = 400000;
	timing = MMC_TIMING_MMC_HS;
	fh_mshc_tuning_set_clock(host, clk, timing, MMC_BUS_WIDTH_1, EMMC_ES_MODE_DISABLE);
	cmd.cmdidx =  0;
	cmd.resp_type = MMC_RSP_NONE;
	cmd.cmdarg = 0;
	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
	}

	do{
		cmd.cmdidx = MMC_CMD_SEND_OP_COND;
		cmd.resp_type = MMC_RSP_R3;
		cmd.cmdarg = 0x40000080;
		sdhci_tuning_send_command(host, &cmd, NULL);
		time_out--;
		if (!time_out){
			pr_err("cmdidx [%d] timeout...\n",cmd.cmdidx);
			return -1;
		}
		udelay(100);
	}while(!(cmd.response[0] & OCR_BUSY));


	cmd.cmdidx =  2;
	cmd.resp_type = MMC_RSP_R2;
	cmd.cmdarg = 0;
	err = sdhci_tuning_send_command(host, &cmd, NULL);
	if (err){
		pr_err("!!!!!!cmd idx[%d] err ret is %x\n",cmd.cmdidx,err);
	}
	//400K cid must be ok~~~
	memcpy(ret_cid, cmd.response, 16);

	translate_mmcid_to_string(emmc_name, cmd.response);
	pr_err("tuning got emmc_name is %s\n",emmc_name);
	//set rel id
	return 0;
}


struct mshc_lite_emmc_card_info * emmc_name_to_plat_info(struct mshc_lite_emmc_card_info *info, char *emmc_name){

	struct mshc_lite_emmc_card_info *p_info = info;
	for(; p_info->mmc_name != 0; p_info++){
		//pr_err("process p_info name is %s\n",p_info->mmc_name);
		if(strcmp(emmc_name, p_info->mmc_name) == 0){
			//pr_err("got board tuning [%s]\n", p_info->mmc_name);
			break;
		}
	}
	if(!p_info->mmc_name){
		pr_err("warning :: %s may not support....\n",emmc_name);
		p_info = 0;
	}

	return p_info;
}


void dump_emmc_cardinfo(struct mshc_lite_emmc_card_info *emmc_pinfo){
	pr_err("-------------------------------\n");
	pr_err("mmc_name      : %s\n",emmc_pinfo->mmc_name);
	pr_err("mmc_name_buf  : %s\n",emmc_pinfo->mmc_name_buf);
	pr_err("flag          : %08x\n",emmc_pinfo->flag);
	pr_err("magic         : %08x\n",emmc_pinfo->magic);
	pr_err("sup_mode      : %08x\n",emmc_pinfo->sup_mode);
	if(emmc_pinfo->sup_mode & MSHC_MMC_MODE_HS400ES)
		pr_err("sup_max       : MMC_MODE_HS400ES\n");
	else if(emmc_pinfo->sup_mode & MSHC_MMC_MODE_HS400_1_8V)
		pr_err("sup_max       : MMC_MODE_HS400_1_8V\n");
	else if(emmc_pinfo->sup_mode & MSHC_MMC_MODE_HS200_1_8V)
		pr_err("sup_max       : MMC_MODE_HS200_1_8V\n");
	else
		pr_err("sup_max       : MMC_MODE_HS\n");
	pr_err("es_sup        : %08x\n",emmc_pinfo->es_sup);
	pr_err("hs_tx_data    : %08x\n",emmc_pinfo->hs_tx_data);
	pr_err("hs_rx_cmd     : %08x\n",emmc_pinfo->hs_rx_cmd);
	pr_err("hs_rx_data    : %08x\n",emmc_pinfo->hs_rx_data);
	pr_err("hs200_tx_data : %08x\n",emmc_pinfo->hs200_tx_data);
	pr_err("hs200_rx_cmd  : %08x\n",emmc_pinfo->hs200_rx_cmd);
	pr_err("hs200_rx_data : %08x\n",emmc_pinfo->hs200_rx_data);	
	pr_err("hs400_tx_data : %08x\n",emmc_pinfo->hs400_tx_data);
	pr_err("hs400_rx_cmd  : %08x\n",emmc_pinfo->hs400_rx_cmd);
	pr_err("hs400_rx_data : %08x\n",emmc_pinfo->hs400_rx_data);	
	pr_err("-------------------------------\n");
}




int mshc_auto_get_emmc_tuning_data(struct fh_mshc_lite *p_mshc, u8 *buf){
	int i;
	int rec_index;
	struct mshc_lite_plat_info *plat_info;
	struct mshc_mmc_cmd cmd;
	struct mshc_mmc_data data = {0};
	struct sdhci_host *host;
	plat_info = p_mshc->mshc_plat_info;
	host = p_mshc->bind_host;

	int err;
	err = mmc_auto_tuning_common_init(p_mshc, buf);
	if(err){
		pr_err("mmc common init err..%x\n",err);
		return err;
	}

	err = mmc_tuning_switch(host, EXT_CSD_CMD_SET_NORMAL,
		EXT_CSD_BUS_WIDTH, 0);
	if (err) {
		pr_err("switch width err, err:%d\n", err);
		return err;
	}
	//set hs200 timing and driver strength 0
	err = mmc_tuning_switch(host, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
	if (err) {
		pr_err("switch timing err, err:%d\n", err);
		return err;
	}

	err = mmc_tuning_set_blocklen(host, 512);
	if (err) {
		pr_err("mmc_tuning_set_blocklen err, err:%d\n", err);
		return err;
	}

	//read
	cmd.cmdidx = 17;
	cmd.cmdarg = CONFIG_AUTO_TUNING_EMMC_ADDR;
	cmd.resp_type = MMC_RSP_R1;
	data.dest = buf;
	data.blocks = 1;
	data.blocksize = 512;
	data.flags = MSHC_MMC_DATA_READ;
	err = sdhci_tuning_send_command(host, &cmd, &data);
	if(err){
		pr_err("read err...\n");
		return -1;
	}
	return 0;
}

int fh_mshc_auto_tuning_pata_get(struct fh_mshc_lite *p_mshc){
	int ret;
	struct sdhci_host *host;

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct mshc_lite_emmc_card_info *temp_pinfo = 0;

	host = p_mshc->bind_host;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	if(sizeof(struct mshc_lite_emmc_card_info) > 512){
		pr_err("struct mshc_lite_emmc_card_info should less then 512B\n");
		BUG_ON(1);
	}

	ret = mshc_auto_get_emmc_tuning_data(p_mshc, (u8*)g_mshc_buf);
	if (ret) {
		//pr_err("auto get emmc tuning para err...try auto tuning...\n");
		return AUTO_TUNING_PARA_NULL;
	}
	//here just force to card info..
	temp_pinfo = (struct mshc_lite_emmc_card_info *)g_mshc_buf;


	if (temp_pinfo->magic == AUTO_TUNING_DONE) {
		//pr_err("got tuning done para.....\n");
		memcpy(emmc_pinfo, temp_pinfo, sizeof(struct mshc_lite_emmc_card_info));
		//bind name to self..
		emmc_pinfo->mmc_name = emmc_pinfo->mmc_name_buf;
		//dump got info...
		dump_emmc_cardinfo(emmc_pinfo);
		return AUTO_TUNING_PARA_ALREADY_GOT;
	}
	//if no hs spd tuning data..here force to 0x80..
	pr_err("force hs spd dll to 0x80..\n");
	emmc_pinfo->hs_tx_data = 0x80;
	emmc_pinfo->hs_rx_cmd = 0x80;
	emmc_pinfo->hs_rx_data = 0x80;

	return AUTO_TUNING_PARA_NULL;
}


